home *** CD-ROM | disk | FTP | other *** search
/ TPUG - Toronto PET Users Group / TPUG Users Group CD / TPUG Users Group CD.iso / AMIGA / (A)U / (A)U2.ADF / PcView / VIEW.C < prev    next >
Text File  |  1989-06-03  |  15KB  |  505 lines

  1. /* ViewPC - IFF File Viewer - (c) 1988 Dallas John Hodgson
  2.  
  3.    This program must be compiled under memory model "compact" or larger in
  4.    order to fetch the correct allocation libraries. This is to avoid problems
  5.    when loading in pictures of 64K in size. View requires the library
  6.    "EGAVGA.obj" to be linked in at compile time.
  7.  
  8.             Compiled under Turbo C 1.5
  9.  
  10.                SHAREWARE IS FOR THE PUBLIC GOOD. SUPPORT IT!
  11. */
  12.  
  13. #include <stdio.h>
  14. #include <stdlib.h>
  15. #include <alloc.h>
  16. #include <graphics.h>
  17. #include "view.h"
  18.  
  19. #define SafeRead(file,buf,len) if (!fread(buf,len,1,file)) { puts("disk error!"); fclose(file); return(NULL); }
  20.  
  21. /* The following routines are required because Intel machines are little-endian;
  22.    i.e., constants are stored in reverse order. Since the IFF spec was
  23.    originally designed for Motorola CPU's, ANY time we read a numerical
  24.    value larger than a char we had better reverse the result! */
  25.  
  26. #define REVERSE(a) Reverse_Constant(&a,sizeof(a))
  27.  
  28. struct Screen screen;
  29. char planes[MAXPLANES][LINELEN];
  30. int ega_remap[256],
  31.     debug,
  32.     scaling=1, /* scaling ON by default */
  33.     remap=1,   /* remapping ON by default */
  34.     pal=1;     /* palette zapping allowed by default */
  35.  
  36. struct palettetype palette; /* used to hold our default palette */
  37.  
  38. /* RGB 2-bit analog to EGA palette remap tables */
  39.  
  40. static char r_map[4] = { 0,32,4,36 },
  41.             g_map[4] = { 0,16,2,18 },
  42.         b_map[4] = { 0,8,1,9 };
  43.  
  44. /************************************************************************
  45. *                                                                       *
  46. *    Routine name(s) : ReadILBM()                                       *
  47. *    Author          : D. John Hodgson                                  *
  48. *    Environment     : Turbo "C" 1.5, small model                       *
  49. *                                                                       *
  50. *    ReadILBM attempts to read an IFF ILBM file into memory. Returns    *
  51. *    a screen pointer if successful, otherwise NULL.                    *
  52. *                                                                       *
  53. *    LIMITATIONS : no CATS/LISTS/PROPS. CAMG chunks,masking supported.  *
  54. ************************************************************************/
  55.  
  56. struct Screen *ReadILBM(fspec)
  57. char *fspec; /* MS-DOS filename */
  58. {
  59.   FILE *fp;
  60.  
  61.   BitMapHeader bmhd;
  62.   Chunk header;
  63.   long id;
  64.  
  65.   int i;
  66.  
  67.   if (!(fp=fopen(fspec,"rb"))) {
  68.     puts("Error opening file.");
  69.     return(NULL);
  70.   }
  71.  
  72.   SafeRead(fp,&header,sizeof(header));
  73.   if (header.ckID!=ID_FORM) { fclose(fp); return(NULL); }
  74.  
  75.   SafeRead(fp,&id,sizeof(id));
  76.   if (id!=ID_ILBM) { fclose(fp); return(NULL); }
  77.  
  78.   for (;;) {
  79.     SafeRead(fp,&header,sizeof(header));
  80.     REVERSE(header.ckSize);
  81.  
  82.     if (header.ckID==ID_BODY) break;
  83.  
  84.     switch(header.ckID) {
  85.       case ID_BMHD: SafeRead(fp,&bmhd,sizeof(bmhd));
  86.             REVERSE(bmhd.w);
  87.             REVERSE(bmhd.h);
  88.             REVERSE(bmhd.x);
  89.                     REVERSE(bmhd.y);
  90.                     break;
  91.  
  92.       case ID_CMAP: SafeRead(fp,&screen.colormap[0][0],header.ckSize);
  93.                     screen.colors=header.ckSize/3;
  94.                     break;
  95.  
  96.       case ID_CAMG: SafeRead(fp,&screen.ViewModes,header.ckSize);
  97.             REVERSE(screen.ViewModes);
  98.                     break;
  99.  
  100.       /* skip unknown ID's */
  101.  
  102.       default:      fseek(fp,ROUNDODDUP(header.ckSize),SEEK_CUR);
  103.     }
  104.   }
  105.  
  106.   /* ID_BODY reached : read planes into RAM for ease of decompression */
  107.  
  108.   if (!(screen.buffer=farmalloc(header.ckSize))) {
  109.     puts("Insufficient memory for picture allocation.");
  110.     fclose(fp);
  111.     return(NULL);
  112.   }
  113.  
  114.   /* this special is necesary to overcome fread()'s 64K size limitation. */
  115.  
  116.   BigRead(fp,screen.buffer,header.ckSize);
  117.  
  118.   fclose(fp);
  119.  
  120.   /* reduce 8-bit color register values to most significant 4 bits */
  121.  
  122.   for (i=0;i<screen.colors;i++) {
  123.     screen.colormap[i][RGB_RED]/=16; /* BUG! >>=4 SHOULD WORK! */
  124.     screen.colormap[i][RGB_GREEN]/=16;
  125.     screen.colormap[i][RGB_BLUE]/=16;
  126.   }
  127.  
  128.   screen.width=bmhd.w;
  129.   screen.height=bmhd.h;
  130.   screen.compression=bmhd.compression;
  131.   screen.mask=bmhd.masking;
  132.  
  133.   /* add the masked plane to the interleaved plane cnt, if present */
  134.  
  135.   screen.planes=bmhd.nPlanes+(screen.mask==mskHasMask);
  136.  
  137.   return(&screen);
  138. }
  139.  
  140. /* this buffer is TOO BIG to be declared locally! */
  141.  
  142. #define BUFSIZE 40000
  143. char buf[BUFSIZE];
  144.  
  145. BigRead(FILE *file,unsigned char huge *buffer,unsigned long len)
  146. {
  147.   /* Use this routine to perform fast reads into a HUGE buffer */
  148.  
  149.   unsigned int size;
  150.   char *ptr;
  151.  
  152.   while (len) {
  153.     if (len>=BUFSIZE) { /* 64K max buffer for fread() */
  154.       size=BUFSIZE;
  155.       SafeRead(file,buf,size);
  156.     }
  157.     else {
  158.       size=len;
  159.       SafeRead(file,buf,size);
  160.     }
  161.  
  162.     len-=size; ptr=buf;
  163.  
  164.     while (size--) *buffer++=*ptr++; /* local to HUGE buffer copy */
  165.   }
  166. }
  167.  
  168. Reverse_Constant(value,size)
  169. char value[];
  170. int size;
  171. {
  172.   int i;
  173.   char buf[sizeof(long)]; /* big enough to hold a char, short, or long */
  174.  
  175.   movmem(value,&buf,size); /* Copy the Motorola value to our buffer */
  176.  
  177.   size--; /* size must be from [0..n-1] for array processing */
  178.  
  179.   for (i=0;i<=size;i++) value[i]=buf[size-i]; /* & copy it back, Intel-style */
  180. }
  181.  
  182. Expand() /* pixel line decompress/deinterleave */
  183. {
  184.   short plane,linelen,rowbytes,i,width=screen.width;
  185.   char n,*destbuf,huge *sourcebuf=screen.buffer;
  186.  
  187.   /* roundup width to a multiple of 16 pixels, if necessary; important
  188.      for deinterleaving brushes correctly! */
  189.  
  190.   if (width%16) width=(((width/16)+1)*16);
  191.  
  192.   linelen=width/8;
  193.  
  194.   for (i=0;i<screen.height;i++) { /* process n lines/screen */
  195.     for (plane=0;plane<screen.planes;plane++) { /* process n planes/line */
  196.       destbuf=&planes[plane][0];
  197.  
  198.       if (screen.compression==cmpByteRun1) { /* compressed screen? */
  199.         rowbytes=linelen;
  200.  
  201.         while (rowbytes) { /* unpack until 1 scan-line complete */
  202.           n=*sourcebuf++;  /* fetch block run marker */
  203.  
  204.           /* uncompressed block? copy n bytes verbatim */
  205.           if (n>=0) {
  206.         movmem(sourcebuf,destbuf,(unsigned int)++n);
  207.             rowbytes-=n;
  208.             destbuf+=n; sourcebuf+=n;
  209.           }
  210.           else { /* compressed block? expand n duplicate bytes */
  211.             n=-n+1; rowbytes-=n;
  212.         setmem(destbuf,n,(unsigned int)*sourcebuf++);
  213.             destbuf+=n;
  214.           }
  215.         } /* finish unpacking line */
  216.       }
  217.       else { /* uncompressed? just copy */
  218.         movmem(sourcebuf,destbuf,(unsigned int)linelen);
  219.         sourcebuf+=linelen; destbuf+=linelen;
  220.       }
  221.     } /* finish interleaved planes */
  222.  
  223.     /* line assembled, so flush pixels out */
  224.  
  225.     flush_pixels(i,linelen);
  226.  
  227.   } /* finish lines */
  228. }
  229.  
  230. flush_pixels(line,bytelen)
  231. int line,bytelen;
  232. {
  233.   int index,bit,p,color,px;
  234.  
  235.   for (index=0;index<bytelen;index++) {
  236.     for (bit=0;bit<8;bit++) {
  237.  
  238.       for (color=p=0;p<screen.planes;p++) {
  239.         if (planes[p][index] & (1<<bit))
  240.       color|=(1<<p);
  241.       }
  242.  
  243.       /* Remap color according to closest palette available */
  244.  
  245.       if (screen.mask==mskHasMask) color&=~(1<<p-1); /* remove mask effect on color */
  246.  
  247.       if (remap) color=ega_remap[color];
  248.  
  249.       /* Stretch lo-res pictures in the x direction if necessary.
  250.      Note that the pixels in a byte need to be spit out in
  251.      reverse order; i.e., the MSB represents the leftmost pixel. */
  252.  
  253.       px=index*8+(7-bit);
  254.  
  255.       /* Don't let the padded pixel width exceed the original picture width!
  256.          Any pixel padding is for the purposes of adhering to IFF
  257.      decompression ONLY. For display, we must remain true to the original
  258.      width. (otherwise, brushes will have the padding displayed!) */
  259.  
  260.       if (px<screen.width) { /* the real, unpadded width */
  261.  
  262.         if (!scaling || screen.width>LOWIDTH) putpixel(px,line,color);
  263.         else { /* stretch 320-wide pics to meet 640 resolution */
  264.           px<<=1;
  265.       putpixel(px,line,color);
  266.         putpixel(px+1,line,color);
  267.         }
  268.       }
  269.  
  270.     } /* bit loop */
  271.   } /* byte loop */
  272.  
  273. }
  274.  
  275. remap_colors()
  276. {
  277.   int color,i,delta,bestpick,bestdelta;
  278.   char sr,sg,sb,dr,dg,db,br,bg,bb;
  279.  
  280.   static char ega_colors[16][3] = {
  281.     { 0,0,0 },        /* black */
  282.     { 0,0,10 },        /* blue */
  283.     { 0,10,0 },        /* green */
  284.     { 0,10,10 },    /* cyan */
  285.     { 10,0,0 },        /* red */
  286.     { 10,0,10 },    /* magenta */
  287.     { 10,5,0 },        /* brown */
  288.     { 10,10,10 },    /* lt. grey */
  289.     { 5,5,5 },        /* dk. grey */
  290.     { 5,5,15 },        /* lt. blue */
  291.     { 5,15,5 },        /* lt. green */
  292.     { 5,15,15 },    /* lt. cyan */
  293.     { 15,5,5 },        /* lt. red */
  294.     { 15,5,15 },    /* lt. magenta */
  295.     { 15,15,5 },    /* yellow */
  296.     { 15,15,15 }    /* white */
  297.   };
  298.  
  299.   /* step through the colors in our picture, remapping to the closest
  300.      possible equivalent in the EGA color scheme. This will also
  301.      perform color reduction if the picture has more bitplanes (colors)
  302.      than EGA will allow! */
  303.  
  304.   for (color=0;color<screen.colors;color++) {
  305.  
  306.     bestpick=0; bestdelta=0xfff;
  307.  
  308.     for (i=0;i<16;i++) {
  309.  
  310.       sr=screen.colormap[color][RGB_RED];
  311.       sg=screen.colormap[color][RGB_GREEN];
  312.       sb=screen.colormap[color][RGB_BLUE];
  313.  
  314.       dr=ega_colors[i][RGB_RED];
  315.       dg=ega_colors[i][RGB_GREEN];
  316.       db=ega_colors[i][RGB_BLUE];
  317.  
  318.       /* don't even ATTEMPT to remap colors that are an exact match! */
  319.  
  320.       if (sr==dr && sg==dg && sb==db) {
  321.     bestpick=i;
  322.     break;
  323.       }
  324.  
  325.       delta=
  326.       abs(bias(sr)-ega_colors[i][RGB_RED])+
  327.           abs(bias(sg)-ega_colors[i][RGB_GREEN])+
  328.           abs(bias(sb)-ega_colors[i][RGB_BLUE]);
  329.  
  330.       if (delta<bestdelta) {
  331.         bestdelta=delta;
  332.         bestpick=i;
  333.       }
  334.     }
  335.  
  336.     ega_remap[color]=bestpick;
  337.  
  338.     if (debug) {
  339.       br=ega_colors[bestpick][RGB_RED];
  340.       bg=ega_colors[bestpick][RGB_GREEN];
  341.       bb=ega_colors[bestpick][RGB_BLUE];
  342.  
  343.       printf("source color %d : %x,%x,%x remapped to EGA color %d (%x,%x,%x)\n",
  344.         color,sr,sg,sb,r_map[br]+g_map[bg]+b_map[bb],br,bg,bb);
  345.     }
  346.  
  347.   }
  348. }
  349.  
  350. bias(color)
  351. int color;
  352. {
  353.   /* reduce color choices to the EGA choices of none, normal or bright
  354.      simplifies the color remapping process to an exact match within
  355.      the EGA palette of primaries. */
  356.  
  357.   if (color<5) return(NULL);
  358.  
  359.   if (color<0x0b) return(0x0a);
  360.  
  361.   return(0x0f);
  362. }
  363.  
  364. OpenScreen()
  365. {
  366.    /* our default DPaint-II palette of primaries */
  367.  
  368.    static struct palettetype dpaint = {
  369.      16, { 0,1,2,3,4,5,20,7,56,57,58,59,60,61,62,63 }
  370.    };
  371.  
  372.    int driver=EGA,mode=EGALO; /* 640 x 200 x 16 */
  373.    int result,i;
  374.  
  375.    if (screen.height>LOHEIGHT) mode=EGAHI;
  376.  
  377.    /* acknowledge the existence of our pre-linked EGA driver */
  378.  
  379.    if (registerbgidriver(EGAVGA_driver) < 0) return(NULL);
  380.  
  381.    initgraph(&driver,&mode,"");
  382.  
  383.    result=graphresult();
  384.  
  385.    if (result!=grOk) {
  386.      printf("Error : %s\n",grapherrormsg(result));
  387.      return(NULL);
  388.    }
  389.  
  390.    /* save the TurboC default palette so we can restore it later */
  391.  
  392.    getpalette(&palette);
  393.  
  394.    /* We have one chance to display a picture in EGA mode without
  395.       remapping. Pictures in EGAHI with 16 colors or less can be
  396.       displayed simply by zapping the palette, which supports a
  397.       whopping 6 bits per color register. No need to do this for EGALO,
  398.       which only supports a 16-color palette to begin with.
  399.  
  400.       NOTE #1 : We could display EGALO pictures in EGAHI resolution,
  401.       except that we would lose 50 lines of image expanding a 200-line
  402.       picture to 400 lines. Drawing would be twice as slow, and screen
  403.       captures would be twice the size.
  404.  
  405.       NOTE #2 : Zapping color registers only preserves the most significant
  406.       two bits of RGB color present in the source image. This is fine for
  407.       IBM pics, but colors may be less pleasant than the AI remapping scheme
  408.       used when other pictures are displayed. Then again, they may be better.
  409.     */
  410.  
  411.    if (screen.planes <= 4 && mode==EGAHI && pal) {
  412.      for (i=0;i<screen.colors;i++) {
  413.        result=r_map[screen.colormap[i][RGB_RED]/4] +
  414.           g_map[screen.colormap[i][RGB_GREEN]/4] +
  415.           b_map[screen.colormap[i][RGB_BLUE]/4];
  416.  
  417.        setpalette(i,result);
  418.  
  419.        if (debug)
  420.           printf("Palette color %d = set to EGA %d (%d,%d,%d)\n",i,result,
  421.           screen.colormap[i][RGB_RED]/4,screen.colormap[i][RGB_GREEN]/4,
  422.           screen.colormap[i][RGB_BLUE]/4);
  423.      }
  424.      remap=0;
  425.    }
  426.  
  427.    if (remap) {
  428.      setallpalette(&dpaint);
  429.      remap_colors();
  430.    }
  431.  
  432.    /* Ideally, we would set do fill the whole screen with the background
  433.       color at this point. For full-screen pictures, this would not be
  434.       necessary; background pixels will be rendered correctly by hand.
  435.       Unfortunately, the Amiga & PC versions disagree on which color should
  436.       be the default background color. For this reason, pictures whose
  437.       mask = maskHasTransparentColor can only render opaquely. */
  438.  
  439.   return(TRUE);
  440. }
  441.  
  442. welcome()
  443. {
  444.     puts("                        Welcome to PC-View!\n\n"
  445.        "The IFF graphic file format, developed in joint development between\n"
  446.        "Electronic Arts and Commodore-Amiga, Inc. is the PC communities'\n"
  447.        "first attempt at a portable graphic standard. Find it in use on\n"
  448.        "the Commodore Amiga, Apple-II GS, IBM-PC, and perhaps others. Use\n"
  449.        "View to display otherwise undisplayable graphics (incl. VGA) on your PC!\n\n"
  450.            "                Usage : view <file> [-s] [-p] [-d]\n\n"
  451.        "Options : -s disables auto horizontal scaling\n"
  452.        "          -p disables auto direct palette mapping\n"
  453.        "          -d displays information about your IFF picture\n\n"
  454.        "PC-View :                 Dallas Hodgson\n"
  455.        "                        351 Kiely Bl, #B304\n"
  456.        "                        San Jose, Ca. 95129\n\n"
  457.        "             Comments & Contributions gladly accepted.\n");
  458.  
  459.     exit(100);
  460. }
  461.  
  462. main(argc,argv)
  463. int argc;
  464. char *argv[];
  465. {
  466.   char *fspec=0;
  467.  
  468.   /* parse up command line */
  469.  
  470.   while (--argc) {
  471.     ++argv;
  472.  
  473.     if (argv[0][0]=='-')
  474.       switch(toupper(argv[0][1])) {
  475.     case 'S': scaling=0; break;
  476.     case 'P': pal=0; break;
  477.     case 'D': debug=1; break;
  478.     default : welcome(); /* unrecognized switch */
  479.       }
  480.     else fspec=argv[0];
  481.   }
  482.  
  483.   if (!fspec) welcome(); /* no filename specified */
  484.  
  485.   puts(TITLE);
  486.  
  487.   if (!ReadILBM(fspec)) exit(100);
  488.  
  489.   if (debug) {
  490.     printf("Width=%d, Height=%d, Planes=%d, Colors=%d\n",
  491.            screen.width,screen.height,screen.planes,screen.colors);
  492.     printf("Compression=0x%x, Masking=0x%x, ViewModes=0x%lx\n",
  493.        screen.compression,screen.mask,screen.ViewModes);
  494.     putchar('\n');
  495.   }
  496.  
  497.   if (OpenScreen()) {
  498.     Expand();
  499.     getchar();
  500.  
  501.     setallpalette(&palette);
  502.     closegraph();
  503.   }
  504. }
  505.